﻿using System;
using TestingConcurrencyBenchmark.Helpers;

namespace TestingConcurrencyBenchmark.Problems.ProducerConsumer
{
    /// <summary>
    /// This class represents Downey's suggested solution to the Producer Consumer problem.
    /// </summary>
    internal class ProducerConsumerSolution : AbstractProducerConsumer
    {
        /// <summary>
        /// A BenchmarkSemaphore to protect the buffer
        /// </summary>
        private readonly BenchmarkSemaphore _mutex;

        public ProducerConsumerSolution()
        {
            _mutex = new BenchmarkSemaphore(1);
        }

        /// <summary>
        /// Invokes an action from the queue if there is one.
        /// If there aren't any actions to perform, a thread will have to wait for _items.
        /// As soon as a producer thread has signeled it has produced something (added something to the queue)
        /// the consumer will dequeue the action from the buffer and invoke it.
        /// </summary>
        public override void Consume()
        {
            Items.WaitOne();
            _mutex.WaitOne();
            Action action = Buffer.Dequeue();
            action.Invoke();
            _mutex.Release();
        }

        /// <summary>
        /// Adds an action to perform on the queue.
        /// First in enqueues the action, then it tells 
        /// the BenchmarkSemaphore "_items" an item has been added to the queue
        /// </summary>
        /// <param name="action"></param>
        public override void Produce(Action action)
        {
            _mutex.WaitOne();
            Buffer.Enqueue(action);
            _mutex.Release();
            Items.Release();
        }
    }
}